#pragma once
// #define IO_HANDLER_DEBUG 1

#include <iostream>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include "Comm.hpp"
#include "Connection.hpp"
#include "Factory.hpp"
#include "ServerHandler.hpp"

class IOHandler
{
    static const int buffersize = 1024;

public:
    IOHandler() {}
    static void Accept(Connection *listenconn)
    {
        if (!listenconn)
            return;

        // errno 在每次进入IO时都要重置
        errno = 0;

        while (true)
        {
            struct sockaddr_in addr;
            socklen_t len = sizeof(addr);
            int newsock = accept(listenconn->GetSockFd(), (struct sockaddr *)&addr, &len);

            if (newsock > 0)
            {
                // 首先将新连接设置为非阻塞
                SetNonBlock(newsock);
                // 其次创建Connection对象
                unique_ptr<Factory> factory = make_unique<Factory>();
                Connection *newconn = factory->BuildConn(newsock, EPOLLIN | EPOLLET, listenconn->GetReactor(), Read, Write, Except);
                newconn->SetClientAddr(addr);
                // 将对象添加到Reactor模块中(也就是添加到Epoll模型以及_connections中被管理)
                newconn->GetReactor()->AddConnection(newconn, EPOLLIN | EPOLLET);
            }
            else
            {
                if (errno == EINTR)
                    continue;
                else if (errno == EAGAIN)
                    break;
                else
                {
                    // 走到这里是真的出错了， 意味着本次监听描述符接收连接错误，
                    break;
                }
            }
        }
    }

    static void Read(Connection *conn)
    {
        if (!conn)
            return;

        // 重置errno，防止旧的errno数据影响本函数的进行
        errno = 0;

        char buffer[buffersize];
        while (true)
        {
            ssize_t n = read(conn->GetSockFd(), buffer, sizeof(buffer) - 1);

            if (n > 0)
            {
                buffer[n] = 0;
                conn->PushReadBuffer(buffer);
            }
            else
            {
                if (errno == EINTR)
                    continue;
                else if (errno == EAGAIN)
                    break;
                else
                {
                    conn->Excepte();
                    return;
                }
            }
        }

        // 走到这里意味着已经读取完了数据，应该开始进行对数据的处理了
        ServerHandler(conn);
        // 处理完数据我们应该试着在连接对象的发送缓冲区不为空的情况下直接发送连接对象中的数据
        if (!conn->OutEmpty())
            conn->Write();
    }

    static void Write(Connection *conn)
    {
        if (!conn)
            return;

        // 重置errno，防止旧的errno数据影响本函数的进行
        errno = 0;
        // std::cout << "write..." << std::endl;
        std::string &out = conn->GetOutBuffer();
#ifdef IO_HANDLER_DEBUG
        LOG(Debug, "debug, %s\n", CSTR(out));
#endif 
        while (true)
        {
            ssize_t n = write(conn->GetSockFd(), CSTR(out), out.size());
#ifdef IO_HANDLER_DEBUG
        LOG(Debug, "debug, %d\n", n);
#endif 
            if (n >= 0)
            {
                out.erase(0, n);
                if (out.empty())
                    break;
            }
            else
            {
                if (errno == EINTR)
                    continue;
                else if (errno == EAGAIN)
                    break;
                else
                {
                    conn->Excepte();
                    return;
                }
            }
        }

        if (!conn->OutEmpty())
        {
            // 这里意味着数据没有发送完，意味着下层发送缓冲区已经被写满了，所以我们要将该连接对象等待写条件就绪交给epoll模型来等待
            conn->GetReactor()->ModConnection(conn, EPOLLIN | EPOLLOUT | EPOLLET);
        }
        else
        {
            // 走到这里意味着上层发送缓冲区的数据发送到下层的发送缓冲区之后，下层仍然没有没发满，则此时取消对该文件描述符的读事件关心
            conn->GetReactor()->ModConnection(conn, EPOLLIN | EPOLLET);
        }

#ifdef IO_HANDLER_DEBUG
        sleep(10000000);
#endif 
    }

    static void Except(Connection *conn)
    {
        if (!conn)
            return;
        // 如果epoll模型在等待一个描述符时除了错误，亦或者是读写时错误，亦或者是对端正常关闭
        // 我们都会进入这个异常处理，来取消双方建立好的连接

        // 将该文件描述符从epoll模型中取消关心
        // 从Reactor管理的_connections中移除这个conn对象
        conn->GetReactor()->DelConnection(conn);

        // 关闭文件描述符
        conn->Close();
        // 释放conn对象
        delete conn;
    }

    ~IOHandler() {}
};